﻿using System;
using System.Collections.Generic;
using HttpServer.Headers;

namespace HttpServer.Authentication
{
    /// <summary>
    /// Provides authentication in the web server.
    /// </summary>
    /// <remarks>
    /// To initiate authentication you just need to throw a Una
    /// </remarks>
    public class AuthenticationProvider
    {
        private readonly Dictionary<string, IAuthenticator> _authenticators = new Dictionary<string, IAuthenticator>();

        /// <summary>
        /// Add a authenticator.
        /// </summary>
        /// <param name="authenticator"></param>
        public void Add(IAuthenticator authenticator)
        {
            _authenticators.Add(authenticator.Scheme, authenticator);
        }

        /// <summary>
        /// Authenticate request.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        /// <remarks>
        /// Requires that a <c>AuthorizationHeader</c> have been sent by the client. If not,
        /// request one by sending a WWW-Authentication header (can be generated by the Challenge method).
        /// </remarks>
        /// <exception cref="InvalidOperationException">Authorization header was not found in the request.</exception>
        /// <exception cref="NotSupportedException">Requested authentication scheme is not supported.</exception>
        public IAuthenticationUser Authenticate(IRequest request)
        {
            var authHeader = request.Headers[AuthorizationHeader.NAME] as AuthorizationHeader;
            if (authHeader == null)
                throw new InvalidOperationException(AuthorizationHeader.NAME + " header was not found in the request.");

            IAuthenticator authenticator;
            if (!_authenticators.TryGetValue(authHeader.Scheme, out authenticator))
                throw new NotSupportedException(authHeader.Scheme + " is not supported (no authenticator was found).");

            return authenticator.Authenticate(authHeader, request.Uri.Host, request.Method.ToString().ToLower());
        }

        /// <summary>
        /// Create a challenge header (WWW-authenticate)
        /// </summary>
        /// <param name="response">Response that the authentication header should be added to</param>
        /// <param name="realm">Realm that the user should authenticate in</param>
        /// <returns>WWW-Authenticate header.</returns>
        /// <remarks>
        /// <para>
        /// Scheme can currently be <c>basic</c> or <c>digest</c>. Basic is not very safe, but easier to use.
        /// Digest is quite safe.
        /// </para><para>
        /// </para>
        /// </remarks>
        /// <exception cref="NotSupportedException">Requested scheme is not supported.</exception>
        public void CreateChallenge(IResponse response, string realm)
        {
            foreach (var authenticator in _authenticators.Values)
            {
                var header= authenticator.CreateChallenge(realm);
                response.Add(header.Name, header);
            }
        }
    }
}